package com.partsoft.dits.transform;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.io.IOUtils;
import org.springframework.util.Assert;

import com.partsoft.dits.ConfigurableBean;
import com.partsoft.dits.exts.ExtendsExecution;
import com.partsoft.dits.message.DataProperty;
import com.partsoft.dits.message.MessageEntity;
import com.partsoft.dits.message.MessageException;
import com.partsoft.dits.message.MessageProcessor;
import com.partsoft.dits.utils.MessageEntityUtils;
import com.partsoft.dits.utils.ReturnExecutionCallback;

import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.MapModel;
import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;

public class BaseTransformReactor implements TransformReactor, ConfigurableBean {

	private static final String PUBLISH_SCRIPT_TEMPLATE = "default_script_template";

	private Configuration transformCfg = new Configuration();

	private Template defaultScriptTemplate;

	private ExtendsExecution extendsExecution;

	private MessageProcessor messageProcessor = MessageProcessor.INSTANCE;

	public void setMessageProcessor(MessageProcessor messageProcessor) {
		this.messageProcessor = messageProcessor;
	}

	public void setExtendsExecution(ExtendsExecution extendsExecution) {
		this.extendsExecution = extendsExecution;
	}

	public String getDefaultScriptContent(List<DataProperty> tableFields) {
		Writer swr = new StringWriter();
		try {
			Map<String, Object> rootMap = new HashMap<String, Object>(1);
			rootMap.put("fields", tableFields);
			defaultScriptTemplate.process(rootMap, swr);
		} catch (Throwable e) {
			throw new IllegalStateException(String.format("获取缺省脚本失败：%s", e.getMessage()), e);
		}
		return swr.toString();
	}

	protected InputStream getDefaultScriptTemplateInputStream() {
		return this.getClass().getResourceAsStream("dits-pstmp.ftl");
	}

	private void loadDefaultPublishScript() {
		transformCfg = new Configuration();
		InputStream is = getDefaultScriptTemplateInputStream();
		InputStreamReader isr = new InputStreamReader(is);
		try {
			defaultScriptTemplate = new Template(PUBLISH_SCRIPT_TEMPLATE, isr, transformCfg);
		} catch (Throwable e) {
			throw new IllegalStateException(String.format("初始化默认脚本模板失败:%s", e.getMessage()), e);
		} finally {
			IOUtils.closeQuietly(isr);
			IOUtils.closeQuietly(is);
		}
	}

	public boolean isAlreadyInit() {
		return transformCfg == null;
	}

	public void initContext() {
		if (isAlreadyInit()) {
			finiContext();
		}
		Assert.notNull(extendsExecution);
		Assert.notNull(messageProcessor);
		synchronized (BaseTransformReactor.class) {
			loadDefaultPublishScript();
		}
	}

	private Template getTransformTemplateFromScriptContent(String scf) {
		Reader isr = new StringReader(scf == null ? "" : scf);
		try {
			// TODO 此处需要进行优化
			return new Template("转换脚本", isr, transformCfg);
		} catch (Throwable e) {
			throw new IllegalStateException(e.getMessage(), e);
		} finally {
			if (isr != null) {
				try {
					isr.close();
				} catch (IOException e) {

				}
			}
		}
	}

	private Object packTransformRootMap(MessageEntity entity) throws ClassNotFoundException {
		Map<String, Object> rootMap = new LinkedHashMap<String, Object>();

		for (String namespace : extendsExecution.getExtendsAliasNamespaces()) {
			Map<String, Object> aliasMap = extendsExecution.getExtendsAliasObjects(namespace);
			if (namespace.length() == 0) {
				for (Entry<String, Object> tempEntry : aliasMap.entrySet()) {
					rootMap.put(tempEntry.getKey(), tempEntry.getValue());
				}
			} else {
				rootMap.put(namespace, new MapModel(aliasMap, (BeansWrapper) ObjectWrapper.BEANS_WRAPPER));
			}
		}

		try {
			rootMap.put("SOURCE", entity);
			rootMap.put("header", ObjectWrapper.BEANS_WRAPPER.wrap(entity.getHeader()));
			rootMap.put("rows", entity.getDatas());
		} catch (Throwable e) {
			throw new IllegalStateException(e.getMessage(), e);
		}

		return Collections.unmodifiableMap(rootMap);
	}

	public void finiContext() {
		synchronized (BaseTransformReactor.class) {
			transformCfg = null;
			defaultScriptTemplate = null;
		}
	}

	public StringBuffer transform(String scriptContent, MessageEntity messageEntity) throws TransformException {
		Assert.notNull(messageEntity);
		TransformExtendsLibraryTask task = new TransformExtendsLibraryTask(this, scriptContent, messageEntity);
		try {
			return extendsExecution.executeInExtends(task);
		} catch (Throwable e) {
			throw new TransformException(String.format("转换时发生错误: %s", e.getMessage()), messageEntity, e);
		}
	}

	public MessageEntity transform(MessageEntity entity, String scriptContent) throws TransformException {
		StringBuffer buffer = transform(scriptContent, entity);
		try {
			return messageProcessor.fromMessageBytes(buffer.toString());
		} catch (Throwable e) {
			throw new TransformException(e.getMessage(), entity, scriptContent, e);
		}
	}

	public MessageEntity validateTransformMessageEntity(MessageEntity sourceMsg, String scriptContent,
			List<DataProperty> expectColumns, boolean allowLose) throws TransformException {
		Assert.notEmpty(expectColumns);
		Assert.notNull(sourceMsg);
		Assert.notEmpty(expectColumns);
		MessageEntity targetMsg = transform(sourceMsg, scriptContent);
		if (!targetMsg.hasData())
			throw new TransformException("转换后的消息格式不正确(缺少行数据)，不能接收入库。", sourceMsg, scriptContent);
		try {
			MessageEntityUtils.validateMessageDataItemExpectColumns(targetMsg, expectColumns, allowLose);
		} catch (MessageException e) {
			throw new TransformException(String.format("转换校验时发生错误:%s", e.getMessage()), targetMsg, e);
		}
		return targetMsg;
	}

	public MessageEntity validateTransformMessageEntity(MessageEntity sourceMsg, String scriptContent,
			List<DataProperty> expectColumns) throws TransformException {
		Assert.notEmpty(expectColumns);
		return validateTransformMessageEntity(sourceMsg, scriptContent, expectColumns, true);
	}

	public MessageEntity validateTransformMessageEntity(MessageEntity sourceMsg, String scriptContent,
			DataProperty[] expectColumns) throws TransformException {
		return validateTransformMessageEntity(sourceMsg, scriptContent, expectColumns, true);
	}

	public MessageEntity validateTransformMessageEntity(MessageEntity sourceMsg, String scriptContent,
			DataProperty[] expectColumns, boolean allowLose) throws TransformException {
		Assert.notEmpty(expectColumns);
		return validateTransformMessageEntity(sourceMsg, scriptContent, Arrays.asList(expectColumns), allowLose);
	}

	public static class TransformExtendsLibraryTask implements ReturnExecutionCallback<StringBuffer> {

		private final BaseTransformReactor reactor;

		private final MessageEntity msgEntity;

		private final String templateText;

		public TransformExtendsLibraryTask(BaseTransformReactor reactor, String template, MessageEntity msgEntity) {
			this.templateText = template;
			this.msgEntity = msgEntity;
			this.reactor = reactor;
		}

		public StringBuffer callback() {
			try {
				Template template = reactor.getTransformTemplateFromScriptContent(templateText);
				StringWriter swr = new StringWriter();
				template.process(reactor.packTransformRootMap(msgEntity), swr);
				return swr.getBuffer();
			} catch (Throwable e) {
				throw new IllegalStateException(e.getMessage(), e);
			}
		}

	}
}
